package hjw.com.config;

import hjw.com.util.JwtUtil;
import hjw.com.util.PackJsn;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>Project:Academic - JwtLoginFilter
 * <p>继承重写 SpringSecurity 已经存在的 账号密码认证过滤器
 * @author IvanWei玮 -- maste
 * @version 1.0
 * @DataTime 2025/1/20 14:13
 * @since 1.8
 */
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    AuthenticationManager authenManger;

    public JwtLoginFilter(AuthenticationManager manager) {
        logger.debug("获取到认证管理器:" + manager);
        this.authenManger = manager;
        //.loginProcessingUrl("/api/login")   //session 模式其效果， 因为重写， 这个不生效
        //在这里， 通知 SpringSecurtiy, 登录的请求路径。
        // super. 明确调用父对象方法
        super.setFilterProcessesUrl("/api/login");
    }

    /**
     * 准备去认证  attempt  尝试  Authentication 认证
     * 返回登录成功的对象， 登录不成功没有对象
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request,
                                                HttpServletResponse response) throws AuthenticationException {
        //获取浏览器登录的账号密码
        String username = obtainUsername(request);
        String pwd = obtainPassword(request);
        //request.getParameter("登录方式")  支持账号登录， 也支持短信验证码登录

        logger.debug("登录的账号:" + username);
        //System.out.println("登录的密码:" + pwd)
        logger.debug("登录的密码:" + pwd);
        //loadUserByUsername(String username)
        //创建 springSecurity 内部的登录令牌
        UsernamePasswordAuthenticationToken  loginToten
                = new UsernamePasswordAuthenticationToken(username, pwd);
        // 让 SpringSecurity 的登录（认证）管理器去认证
        // （认证）管理器去调用 loadUserByUsername(String username)
        Authentication authentication = authenManger.authenticate(loginToten);
        logger.debug("认证成功返回对象， session模式, 放在======>:");
        logger.debug(authentication.toString());
        logger.debug("<=======认证成功返回对象");
        // 返回登录成功的对象， 登录不成功没有对象
        return authentication;

    }

    /**
     * 多态: 继承 重写  向上转型  目的调用子类【对象】方法
     * authenManger.authenticate(loginToten);  认证成功调用如下方法
     *登录成功调用如下方法
     */

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {

        logger.debug("authenManger.authenticate(loginToten);  认证成功调用如下方法");
        logger.warn("登录成功=" + authResult);
        //登录成功对象， 里面有 username, 有角色
        logger.info(authResult.getPrincipal().toString());
        Object principal = authResult.getPrincipal();
// 向下强制转换
        User user = (User) principal;
        logger.info("登录成功=" + user.getUsername());
        logger.info(user.getAuthorities().toString());
        List<String> roles = new ArrayList<>();
        for(GrantedAuthority sga : user.getAuthorities()) {
            logger.debug("sga.toString()=" + sga.toString());
            roles.add(sga.toString());
        }
        logger.info("roles=" + roles);
        //String token = JwtUtil.createToken("139001", roles);
        String token = JwtUtil.createToken(user.getUsername(), roles);
        Map<String,Object> map = new HashMap<>(1);
        map.put("token",token);
        String json =PackJsn.pack(200, "登录成功", map);
        response.setStatus(200);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(json);
        response.flushBuffer();
    }

    /**
     * authenManger.authenticate(loginToten);  认证失败调用如下方法
     * 登录失败调用如下方法
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request,
                                              HttpServletResponse response,
                                              AuthenticationException failed) throws IOException, ServletException {

        logger.debug("authenManger.authenticate(loginToken);  认证失败调用如下方法");
        response.setContentType("text/html;charset=UTF-8");
        response.setStatus(401);
        //@ResponseBody 实现原理， 自己把对象转成JSON 字符串， 再用 response 响应浏览器
        // msg, data  code 3 个成员变量
        logger.warn("登录失败 401");
        String str = PackJsn.pack(401, "认证失败,账号或密码错误", "");
        response.getWriter().write(str);
        response.flushBuffer();
    }

}
